{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "<a href=\"https://colab.research.google.com/drive/1OL1bZ8fGpRpsKJcnyTaZ10YEe-75_0uG?usp=sharing\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"></a>"
      ],
      "metadata": {
        "id": "a9i2tkWZfb6Q"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {
        "id": "j7rJTY5wfYBB"
      },
      "outputs": [],
      "source": [
        "!pip install -qU google-generativeai"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import google.generativeai as genai\n",
        "import getpass\n",
        "import random"
      ],
      "metadata": {
        "id": "BmM2w5bafpJO"
      },
      "execution_count": 2,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Get free-tier Google's Gemini API Key here: https://aistudio.google.com/app/apikey"
      ],
      "metadata": {
        "id": "_txZSf52fucy"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "API_KEY = getpass.getpass(\"Enter your Google API key: \")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "G1jez2Gqfrbc",
        "outputId": "4eeed602-0af9-4be4-a5d4-4339ddd23a9c"
      },
      "execution_count": 3,
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Enter your Google API key: ··········\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "genai.configure(api_key=API_KEY)"
      ],
      "metadata": {
        "id": "FJ6G6l3KgVaI"
      },
      "execution_count": 5,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "class Node:\n",
        "    def __init__(self, state, action=None, parent=None):\n",
        "        self.state = state\n",
        "        self.action = action\n",
        "        self.parent = parent\n",
        "        self.children = []\n",
        "        self.visits = 0\n",
        "        self.value = 0.0\n",
        "\n",
        "    def uct_score(self):\n",
        "        \"\"\"Upper Confidence Bound for Trees\"\"\"\n",
        "        if self.visits == 0:\n",
        "            return float('inf')\n",
        "        exploitation = self.value / self.visits\n",
        "        exploration = 1.4 * (2 * self.parent.visits) ** 0.5 / self.visits\n",
        "        return exploitation + exploration\n",
        "\n",
        "class LATSAgent:\n",
        "    def __init__(self, environment):\n",
        "        self.model = genai.GenerativeModel(\"gemini-2.0-flash\")\n",
        "        self.environment = environment\n",
        "        self.root = Node(environment.get_state())\n",
        "\n",
        "    def select(self, node):\n",
        "        \"\"\"Select best child using UCT\"\"\"\n",
        "        while node.children:\n",
        "            node = max(node.children, key=lambda n: n.uct_score())\n",
        "        return node\n",
        "\n",
        "    def expand(self, node):\n",
        "        \"\"\"Generate possible actions\"\"\"\n",
        "        prompt = f\"\"\"Given this state, suggest 3 possible actions:\n",
        "\n",
        "        State: {node.state}\n",
        "\n",
        "        Actions (one per line):\"\"\"\n",
        "\n",
        "        response = self.model.generate_content(prompt).text\n",
        "        actions = [line.strip() for line in response.split(\"\\n\") if line.strip()][:3]\n",
        "\n",
        "        for action in actions:\n",
        "            child = Node(None, action, node)\n",
        "            node.children.append(child)\n",
        "\n",
        "        return node.children[0] if node.children else node\n",
        "\n",
        "    def simulate(self, node):\n",
        "        \"\"\"Execute action and get new state\"\"\"\n",
        "        if node.action:\n",
        "            print(f\"  🎲 Simulating: {node.action}\")\n",
        "            new_state = self.environment.execute(node.action)\n",
        "            node.state = new_state\n",
        "            return new_state\n",
        "        return node.state\n",
        "\n",
        "    def evaluate(self, state, goal):\n",
        "        \"\"\"LLM evaluates how good the state is\"\"\"\n",
        "        prompt = f\"\"\"Rate this state toward the goal (0-10):\n",
        "\n",
        "        Goal: {goal}\n",
        "        State: {state}\n",
        "\n",
        "        Score (0-10):\"\"\"\n",
        "\n",
        "        response = self.model.generate_content(prompt).text\n",
        "        try:\n",
        "            score = float(response.strip().split()[0])\n",
        "            return min(max(score / 10, 0), 1)  # Normalize to 0-1\n",
        "        except:\n",
        "            return 0.5\n",
        "\n",
        "    def backpropagate(self, node, value):\n",
        "        \"\"\"Update values up the tree\"\"\"\n",
        "        while node:\n",
        "            node.visits += 1\n",
        "            node.value += value\n",
        "            node = node.parent\n",
        "\n",
        "    def search(self, goal, iterations=5):\n",
        "        \"\"\"Run MCTS with real environment interaction\"\"\"\n",
        "        print(f\"\\n{'='*60}\")\n",
        "        print(f\"🌳 LATS Search: {goal}\")\n",
        "        print(f\"{'='*60}\\n\")\n",
        "\n",
        "        for i in range(iterations):\n",
        "            print(f\"--- Iteration {i+1} ---\")\n",
        "\n",
        "            # 1. Selection\n",
        "            node = self.select(self.root)\n",
        "            print(f\"  ↓ Selected node at depth {self._depth(node)}\")\n",
        "\n",
        "            # 2. Expansion\n",
        "            if node.visits > 0:\n",
        "                node = self.expand(node)\n",
        "                print(f\"  ↗ Expanded with action: {node.action}\")\n",
        "\n",
        "            # 3. Simulation\n",
        "            state = self.simulate(node)\n",
        "\n",
        "            # 4. Evaluation\n",
        "            value = self.evaluate(state, goal)\n",
        "            print(f\"  ⭐ Evaluated: {value:.2f}\\n\")\n",
        "\n",
        "            # 5. Backpropagation\n",
        "            self.backpropagate(node, value)\n",
        "\n",
        "        # Return best action\n",
        "        best = max(self.root.children, key=lambda n: n.value / n.visits if n.visits > 0 else 0)\n",
        "        print(f\"{'='*60}\")\n",
        "        print(f\"🏆 Best action: {best.action}\")\n",
        "        print(f\"   Value: {best.value:.2f}, Visits: {best.visits}\")\n",
        "        print(f\"{'='*60}\\n\")\n",
        "\n",
        "        return best.action\n",
        "\n",
        "    def _depth(self, node):\n",
        "        depth = 0\n",
        "        while node.parent:\n",
        "            depth += 1\n",
        "            node = node.parent\n",
        "        return depth\n",
        "\n",
        "    def show_tree(self):\n",
        "        \"\"\"Display tree statistics\"\"\"\n",
        "        print(f\"📊 Tree Stats:\")\n",
        "        print(f\"   Root visits: {self.root.visits}\")\n",
        "        print(f\"   Children: {len(self.root.children)}\")\n",
        "        for i, child in enumerate(self.root.children, 1):\n",
        "            avg = child.value / child.visits if child.visits > 0 else 0\n",
        "            print(f\"   {i}. {child.action[:40]}... | Score: {avg:.2f} | Visits: {child.visits}\")\n",
        "\n",
        "\n",
        "# Simple environment simulator\n",
        "class Environment:\n",
        "    def __init__(self, initial_state):\n",
        "        self.state = initial_state\n",
        "\n",
        "    def get_state(self):\n",
        "        return self.state\n",
        "\n",
        "    def execute(self, action):\n",
        "        \"\"\"Execute action and return new state (simulated)\"\"\"\n",
        "        # Simple simulation: actions modify state\n",
        "        new_state = self.state.copy()\n",
        "\n",
        "        if \"collect\" in action.lower():\n",
        "            new_state[\"resources\"] = new_state.get(\"resources\", 0) + 10\n",
        "        elif \"build\" in action.lower():\n",
        "            new_state[\"structures\"] = new_state.get(\"structures\", 0) + 1\n",
        "            new_state[\"resources\"] = max(0, new_state.get(\"resources\", 0) - 5)\n",
        "        elif \"explore\" in action.lower():\n",
        "            new_state[\"explored\"] = new_state.get(\"explored\", 0) + 1\n",
        "        elif \"attack\" in action.lower():\n",
        "            new_state[\"enemies\"] = max(0, new_state.get(\"enemies\", 3) - 1)\n",
        "        elif \"defend\" in action.lower():\n",
        "            new_state[\"defense\"] = new_state.get(\"defense\", 0) + 1\n",
        "\n",
        "        self.state = new_state\n",
        "        return new_state"
      ],
      "metadata": {
        "id": "2BF3dY2XfyuT"
      },
      "execution_count": 6,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# Example 1: Resource Management\n",
        "print(\"=\"*60)\n",
        "print(\"EXAMPLE 1: Resource Management Game\")\n",
        "print(\"=\"*60)\n",
        "\n",
        "env1 = Environment({\"resources\": 0, \"structures\": 0, \"explored\": 0})\n",
        "agent1 = LATSAgent(env1)\n",
        "\n",
        "best_action = agent1.search(\"Build 2 structures\", iterations=6)\n",
        "agent1.show_tree()\n",
        "\n",
        "\n",
        "# Example 2: Combat Strategy\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"EXAMPLE 2: Combat Strategy\")\n",
        "print(\"=\"*60)\n",
        "\n",
        "env2 = Environment({\"enemies\": 3, \"defense\": 0, \"health\": 100})\n",
        "agent2 = LATSAgent(env2)\n",
        "\n",
        "best_action = agent2.search(\"Defeat all enemies safely\", iterations=6)\n",
        "agent2.show_tree()\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "id": "YaTGLrU4f8J4",
        "outputId": "e8eef405-a865-44d1-ef37-d660dd561143"
      },
      "execution_count": 7,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "============================================================\n",
            "EXAMPLE 1: Resource Management Game\n",
            "============================================================\n",
            "\n",
            "============================================================\n",
            "🌳 LATS Search: Build 2 structures\n",
            "============================================================\n",
            "\n",
            "--- Iteration 1 ---\n",
            "  ↓ Selected node at depth 0\n",
            "  ⭐ Evaluated: 0.00\n",
            "\n",
            "--- Iteration 2 ---\n",
            "  ↓ Selected node at depth 0\n",
            "  ↗ Expanded with action: Here are three possible actions based on the given state, aiming for early game development:\n",
            "  🎲 Simulating: Here are three possible actions based on the given state, aiming for early game development:\n",
            "  ⭐ Evaluated: 0.20\n",
            "\n",
            "--- Iteration 3 ---\n",
            "  ↓ Selected node at depth 1\n",
            "  🎲 Simulating: 1.  **Explore:** (Prioritizes discovering more of the game world and potentially revealing resource locations)\n",
            "  ⭐ Evaluated: 0.80\n",
            "\n",
            "--- Iteration 4 ---\n",
            "  ↓ Selected node at depth 1\n",
            "  🎲 Simulating: 2.  **Gather Resources:** (Focuses on acquiring the fundamental resource needed to build structures or perform other actions)\n",
            "  ⭐ Evaluated: 0.80\n",
            "\n",
            "--- Iteration 5 ---\n",
            "  ↓ Selected node at depth 1\n",
            "  ↗ Expanded with action: Here are 3 possible actions based on the given state:\n",
            "  🎲 Simulating: Here are 3 possible actions based on the given state:\n",
            "  ⭐ Evaluated: 0.80\n",
            "\n",
            "--- Iteration 6 ---\n",
            "  ↓ Selected node at depth 1\n",
            "  ↗ Expanded with action: Here are three possible actions based on the given state:\n",
            "  🎲 Simulating: Here are three possible actions based on the given state:\n",
            "  ⭐ Evaluated: 0.80\n",
            "\n",
            "============================================================\n",
            "🏆 Best action: 1.  **Explore:** (Prioritizes discovering more of the game world and potentially revealing resource locations)\n",
            "   Value: 1.60, Visits: 2\n",
            "============================================================\n",
            "\n",
            "📊 Tree Stats:\n",
            "   Root visits: 6\n",
            "   Children: 3\n",
            "   1. Here are three possible actions based on... | Score: 0.20 | Visits: 1\n",
            "   2. 1.  **Explore:** (Prioritizes discoverin... | Score: 0.80 | Visits: 2\n",
            "   3. 2.  **Gather Resources:** (Focuses on ac... | Score: 0.80 | Visits: 2\n",
            "\n",
            "============================================================\n",
            "EXAMPLE 2: Combat Strategy\n",
            "============================================================\n",
            "\n",
            "============================================================\n",
            "🌳 LATS Search: Defeat all enemies safely\n",
            "============================================================\n",
            "\n",
            "--- Iteration 1 ---\n",
            "  ↓ Selected node at depth 0\n",
            "  ⭐ Evaluated: 0.80\n",
            "\n",
            "--- Iteration 2 ---\n",
            "  ↓ Selected node at depth 0\n",
            "  ↗ Expanded with action: Here are 3 possible actions, keeping in mind the limited information and aiming for general strategic options:\n",
            "  🎲 Simulating: Here are 3 possible actions, keeping in mind the limited information and aiming for general strategic options:\n",
            "  ⭐ Evaluated: 0.80\n",
            "\n",
            "--- Iteration 3 ---\n",
            "  ↓ Selected node at depth 1\n",
            "  🎲 Simulating: 1.  **Attack Enemies**\n",
            "  ⭐ Evaluated: 0.80\n",
            "\n",
            "--- Iteration 4 ---\n",
            "  ↓ Selected node at depth 1\n",
            "  🎲 Simulating: 2.  **Increase Defense**\n",
            "  ⭐ Evaluated: 0.80\n",
            "\n",
            "--- Iteration 5 ---\n",
            "  ↓ Selected node at depth 1\n",
            "  ↗ Expanded with action: Here are 3 possible actions based on the given state:\n",
            "  🎲 Simulating: Here are 3 possible actions based on the given state:\n",
            "  ⭐ Evaluated: 0.80\n",
            "\n",
            "--- Iteration 6 ---\n",
            "  ↓ Selected node at depth 1\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "WARNING:tornado.access:429 POST /v1beta/models/gemini-2.0-flash:generateContent?%24alt=json%3Benum-encoding%3Dint (127.0.0.1) 286.96ms\n"
          ]
        },
        {
          "output_type": "error",
          "ename": "TooManyRequests",
          "evalue": "429 POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?%24alt=json%3Benum-encoding%3Dint: You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits.\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_requests, limit: 15\nPlease retry in 29.307326756s.",
          "traceback": [
            "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
            "\u001b[0;31mTooManyRequests\u001b[0m                           Traceback (most recent call last)",
            "\u001b[0;32m/tmp/ipython-input-3048209547.py\u001b[0m in \u001b[0;36m<cell line: 0>\u001b[0;34m()\u001b[0m\n\u001b[1;32m     19\u001b[0m \u001b[0magent2\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mLATSAgent\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0menv2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     20\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m \u001b[0mbest_action\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0magent2\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msearch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Defeat all enemies safely\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0miterations\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m6\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     22\u001b[0m \u001b[0magent2\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshow_tree\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/tmp/ipython-input-770814383.py\u001b[0m in \u001b[0;36msearch\u001b[0;34m(self, goal, iterations)\u001b[0m\n\u001b[1;32m     92\u001b[0m             \u001b[0;31m# 2. Expansion\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     93\u001b[0m             \u001b[0;32mif\u001b[0m \u001b[0mnode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvisits\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 94\u001b[0;31m                 \u001b[0mnode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexpand\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     95\u001b[0m                 \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"  ↗ Expanded with action: {node.action}\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     96\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/tmp/ipython-input-770814383.py\u001b[0m in \u001b[0;36mexpand\u001b[0;34m(self, node)\u001b[0m\n\u001b[1;32m     36\u001b[0m         Actions (one per line):\"\"\"\n\u001b[1;32m     37\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 38\u001b[0;31m         \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgenerate_content\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprompt\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtext\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     39\u001b[0m         \u001b[0mactions\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mline\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstrip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mline\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mresponse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"\\n\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mline\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstrip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     40\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/generativeai/generative_models.py\u001b[0m in \u001b[0;36mgenerate_content\u001b[0;34m(self, contents, generation_config, safety_settings, stream, tools, tool_config, request_options)\u001b[0m\n\u001b[1;32m    329\u001b[0m                 \u001b[0;32mreturn\u001b[0m \u001b[0mgeneration_types\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mGenerateContentResponse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfrom_iterator\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0miterator\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    330\u001b[0m             \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 331\u001b[0;31m                 response = self._client.generate_content(\n\u001b[0m\u001b[1;32m    332\u001b[0m                     \u001b[0mrequest\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    333\u001b[0m                     \u001b[0;34m**\u001b[0m\u001b[0mrequest_options\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/ai/generativelanguage_v1beta/services/generative_service/client.py\u001b[0m in \u001b[0;36mgenerate_content\u001b[0;34m(self, request, model, contents, retry, timeout, metadata)\u001b[0m\n\u001b[1;32m    833\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    834\u001b[0m         \u001b[0;31m# Send the request.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 835\u001b[0;31m         response = rpc(\n\u001b[0m\u001b[1;32m    836\u001b[0m             \u001b[0mrequest\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    837\u001b[0m             \u001b[0mretry\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mretry\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/gapic_v1/method.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, timeout, retry, compression, *args, **kwargs)\u001b[0m\n\u001b[1;32m    129\u001b[0m             \u001b[0mkwargs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"compression\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcompression\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    130\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 131\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mwrapped_func\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    132\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    133\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/retry/retry_unary.py\u001b[0m in \u001b[0;36mretry_wrapped_func\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m    292\u001b[0m                 \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_initial\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_maximum\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiplier\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_multiplier\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    293\u001b[0m             )\n\u001b[0;32m--> 294\u001b[0;31m             return retry_target(\n\u001b[0m\u001b[1;32m    295\u001b[0m                 \u001b[0mtarget\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    296\u001b[0m                 \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_predicate\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/retry/retry_unary.py\u001b[0m in \u001b[0;36mretry_target\u001b[0;34m(target, predicate, sleep_generator, timeout, on_error, exception_factory, **kwargs)\u001b[0m\n\u001b[1;32m    154\u001b[0m         \u001b[0;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mexc\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    155\u001b[0m             \u001b[0;31m# defer to shared logic for handling errors\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 156\u001b[0;31m             next_sleep = _retry_error_helper(\n\u001b[0m\u001b[1;32m    157\u001b[0m                 \u001b[0mexc\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    158\u001b[0m                 \u001b[0mdeadline\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/retry/retry_base.py\u001b[0m in \u001b[0;36m_retry_error_helper\u001b[0;34m(exc, deadline, sleep_iterator, error_list, predicate_fn, on_error_fn, exc_factory_fn, original_timeout)\u001b[0m\n\u001b[1;32m    212\u001b[0m             \u001b[0moriginal_timeout\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    213\u001b[0m         )\n\u001b[0;32m--> 214\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0mfinal_exc\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0msource_exc\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    215\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mon_error_fn\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    216\u001b[0m         \u001b[0mon_error_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/retry/retry_unary.py\u001b[0m in \u001b[0;36mretry_target\u001b[0;34m(target, predicate, sleep_generator, timeout, on_error, exception_factory, **kwargs)\u001b[0m\n\u001b[1;32m    145\u001b[0m     \u001b[0;32mwhile\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    146\u001b[0m         \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 147\u001b[0;31m             \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtarget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    148\u001b[0m             \u001b[0;32mif\u001b[0m \u001b[0minspect\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0misawaitable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    149\u001b[0m                 \u001b[0mwarnings\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwarn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_ASYNC_RETRY_WARNING\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/timeout.py\u001b[0m in \u001b[0;36mfunc_with_timeout\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m    128\u001b[0m                 \u001b[0mkwargs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"timeout\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mremaining_timeout\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    129\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 130\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    131\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    132\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mfunc_with_timeout\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/grpc_helpers.py\u001b[0m in \u001b[0;36merror_remapped_callable\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m     73\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0merror_remapped_callable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     74\u001b[0m         \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 75\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0mcallable_\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     76\u001b[0m         \u001b[0;32mexcept\u001b[0m \u001b[0mgrpc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mRpcError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mexc\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     77\u001b[0m             \u001b[0;32mraise\u001b[0m \u001b[0mexceptions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfrom_grpc_error\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexc\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mexc\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/ai/generativelanguage_v1beta/services/generative_service/transports/rest.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, request, retry, timeout, metadata)\u001b[0m\n\u001b[1;32m   1159\u001b[0m             \u001b[0;31m# subclass.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1160\u001b[0m             \u001b[0;32mif\u001b[0m \u001b[0mresponse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstatus_code\u001b[0m \u001b[0;34m>=\u001b[0m \u001b[0;36m400\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1161\u001b[0;31m                 \u001b[0;32mraise\u001b[0m \u001b[0mcore_exceptions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfrom_http_response\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresponse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   1162\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1163\u001b[0m             \u001b[0;31m# Return the response\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;31mTooManyRequests\u001b[0m: 429 POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?%24alt=json%3Benum-encoding%3Dint: You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits.\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_requests, limit: 15\nPlease retry in 29.307326756s."
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "MWgcIXXjgaIK"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}